package com.dayuanit.dy17.atm.atmbackoffice.service.impl;

import com.dayuanit.dy17.atm.atmbackoffice.dto.PowerDTO;
import com.dayuanit.dy17.atm.atmbackoffice.entity.Power;
import com.dayuanit.dy17.atm.atmbackoffice.entity.User;
import com.dayuanit.dy17.atm.atmbackoffice.exception.BizException;
import com.dayuanit.dy17.atm.atmbackoffice.mapper.CardMapper;
import com.dayuanit.dy17.atm.atmbackoffice.mapper.PowerMapper;
import com.dayuanit.dy17.atm.atmbackoffice.mapper.UserMapper;
import com.dayuanit.dy17.atm.atmbackoffice.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service("userServicexxxx")
public class UserServiceImpl implements IUserService, InitializingBean, BeanNameAware, BeanFactoryAware {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PowerMapper powerMapper;

    public UserServiceImpl() {
        System.out.println("--------UserServiceImpl---------" + this.getClass().getName()) ;
        //此时，属性还没有注入，所以mapper都是null
//        System.out.println("--------UserServiceImpl--------- mapper=" + powerMapper.getClass().getName()) ;
    }

    //InitializingBean 重写   当UserServiceImpl对象创建成功，且属性注入成功后，会执行此方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("--------UserServiceImpl--------afterPropertiesSet mapper=" + powerMapper.getClass().getName()) ;
    }

    //重写了BeanNameAware接口的方法
    @Override
    public void setBeanName(String s) {
        System.out.println("--------UserServiceImpl------- beanName=" + s);
    }

    //重写了BeanFactoryAware接口方法
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("--------UserServiceImpl------- beanFactory=" + beanFactory.getClass().getName());
        CardMapper cardMapper = beanFactory.getBean(CardMapper.class);
        System.out.println("cardMapper=" + cardMapper.getClass().getName());
    }

    @Override
    public void createUser(String username, String password) {
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            throw new BizException("有必填参数为空");
        }

        User user = userMapper.getByUsername(username);
        if (null != user) {
            throw new BizException("用户已存在");
        }

        //业务不一样，但遇到的问题总是惊人的相似，解决方案也是类似的。

        //注意：由于数据库还木有数据，无法使用悲观锁或者乐观锁。

        //防止重复提交解决方案：页面重定向、redis分布式锁以及数据库唯一索引。
        //方案一：使用redis的分布式锁 setNX
        //方案二：数据提供的唯一索引，防止数据重复。

        user = new User();
        user.setPassword(password);
        user.setUsername(username);
        userMapper.insert(user);

        System.out.println(">>>userId=" + user.getId());
    }

    @Override
    public List<PowerDTO> listUserMenu(long userId) {
        List<Power> powers = powerMapper.listMenuByUserId(userId);

        //JDK8提供的新特性 lambda表达式和流式写法
        List<PowerDTO> firstMenu = powers.stream()
                .filter(e -> e.getMenu() == 1 && e.getLevel() == 1)
                .map(e -> {
                    return new PowerDTO(e.getId(), e.getPowerValue());
                }).collect(Collectors.toList());

        List<PowerDTO> secondMenu = powers.stream()
                .filter(e -> e.getMenu() == 1 && e.getLevel() == 2)
                .map(e -> new PowerDTO(e.getId(), e.getPowerValue(), e.getParentId(), e.getUrl()))
                .collect(Collectors.toList());

        //两层for循环 时间复杂度是n^2，效率比较低；但是在工程上，也要看数据量，如果量少，也是可行的。
        //n^3 > n^2 > nlogn > n > logn > 1
        for (PowerDTO parentMenu : firstMenu) {
            List<PowerDTO> childs = new ArrayList<>();
            parentMenu.setChild(childs);

            //两层for循环 时间复杂度N^2  可以通过散列表进行优化
            for (PowerDTO childMenu : secondMenu) {
                if (childMenu.getParentId().equals(parentMenu.getId())) {
                    childs.add(childMenu);
                }
            }
        }
        return firstMenu;
    }

    @Override
    public List<PowerDTO> listUserMenu2(long userId) {
        List<Power> powers = powerMapper.listMenuByUserId(userId);
        //方案一：去重
        powers = powers.stream().distinct().collect(Collectors.toList());

        //方案二：hashSet去重
//        HashSet<Power> powerSet = new HashSet<>();
//        for (Power p : powers) {
//            powerSet.add(p);
//        }

        //获取一级目录
        List<PowerDTO> firstMenus = powers.stream()
                .filter(e -> e.getMenu() == 1 && e.getLevel() == 1)
                .map(e -> new PowerDTO(e.getId(), e.getPowerValue(), e.getParentId(), e.getUrl()))
                .collect(Collectors.toList());

        //将二级目录分组 以父ID分组  用K-V方式；k-父ID v-对应的二级目录List
        Map<Long, List<PowerDTO>> secondMenuMap = new HashMap<>();

        for (Power power : powers) {
            if (power.getMenu() == 1 && power.getLevel() == 2) {
                List<PowerDTO> secondDtos = secondMenuMap.get(power.getParentId());
                if (null == secondDtos) {
                    secondDtos = new ArrayList();
                    secondMenuMap.put(power.getParentId(), secondDtos);
                }
                secondDtos.add(new PowerDTO(power.getId(), power.getPowerValue(), power.getParentId(), power.getUrl()));
            }
        }

        //时间复杂度：O(N)
        for (PowerDTO firstMenu : firstMenus) {
                firstMenu.setChild(secondMenuMap.get(firstMenu.getId()));
        }

        return firstMenus;
    }

    @Override
    public List<PowerDTO> listUserMenu3(long userId) {
        //由于用户可能有多个角色，而多个角色之间可能会有权限的交集，所以要去重。
        List<Power> powers = powerMapper.listMenuByUserId(userId);

        //通过stream流去掉重复元素，底层原理是equals和hashCode协同。
        powers = powers.stream().distinct().collect(Collectors.toList());

        //第一级程菜单
        List<PowerDTO> firstMenus = powers.stream()
                .filter(e -> e.getMenu() == 1 && e.getLevel() == 1)
                .map(e -> new PowerDTO(e.getId(), e.getPowerValue(), e.getParentId(), ""))
                .collect(Collectors.toList());

        //为二级导航栏分组 按照父ID分组  k-父ID  v-当前父菜单对应的二级菜单集合
        Map<Long, List<PowerDTO>> secondMenuMap = powers.stream()
                .filter(e -> e.getMenu() == 1 && e.getLevel() == 2)
                .map(e -> new PowerDTO(e.getId(), e.getPowerValue(), e.getParentId(), e.getUrl()))
                .collect(Collectors.groupingBy(e -> e.getParentId()));

        //时间复杂度 O(n)
        for (PowerDTO parentMenu : firstMenus) {
            parentMenu.setChild(secondMenuMap.get(parentMenu.getId()));
        }

        return firstMenus;
    }

    @Override
    public User login(String username, String password) {
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            throw new BizException("有必填参数为空");
        }

        User user = userMapper.getByUsername(username);
        if (user == null) {
            throw new BizException("用户不存在或者密码错误");
        }

        if (!user.getPassword().equals(password)) {
            throw new BizException("用户不存在或者密码错误");
        }

        return user;
    }


}
